var crypto = require('crypto')
var path = require("path")
var fs = require('fs');
var autodb = require('../../db/auto.db')
var config = require('../../config')
var async = require('async')
var wfi = require('../wav-file-info')
var logger = require('../../logger')
var emit = require('../../emit')

function log() {
    console.log(arguments);
}

String.prototype.startWith = function (s) {
    if (s == null || s == "" || this.length == 0 || s.length > this.length)
        return false;
    return (this.substr(0, s.length) == s);
}

String.prototype.endWith = function (s) {
    if (s == null || s == "" || this.length == 0 || s.length > this.length)
        return false;
    return (this.substring(this.length - s.length) == s);
}

String.prototype.isNumber = function () {
    var reg = /^[0-9]+\.?[0-9]*$/;
    return reg.test(this)
}

function movefile(src, dst, cb) {
    log('movefile:' + src + '->' + dst);
    if (config.debug) {
        cb(null)
    } else {
        fs.rename(src, dst, (err) => {
            cb(err);
        });
    }
}

//把文件名按/和_拆分，作为关键字
function parseSoundTag(filename, callback) {
    let dirname = path.dirname(filename);
    let basename = path.basename(filename);
    let ext = path.extname(basename);
    let withoutext = basename.substring(0, basename.length - ext.length);
    let keys = withoutext.split('_');
    let paths = dirname.split(path.sep);
    let tags = new Set();
    if (paths.length > 1) {
        let maintag = paths[0].toLowerCase().trim();
        let childtag = paths[1].toLowerCase().trim();
        for (var i = 2; i < paths.length; i++) {
            var key = paths[i].toLowerCase().trim();
            if (!key.isNumber()) {
                tags.add(key);
            }
        }
        for (var i = 0; i < keys.length; i++) {
            var key = keys[i].toLowerCase().trim();
            if (!key.isNumber() && key != maintag && key != childtag) {
                tags.add(key);
            }
        }
        callback(null, maintag, childtag, tags);
    } else {
        callback(filename + ' is invalid. must like UI_BUTTON_xx_xxx.wav');
    }
}

function parseMusicTag(filename, callback) {
    let basename = path.basename(filename);
    let ext = path.extname(basename);
    let withoutext = basename.substring(0, basename.length - ext.length);
    let keys = withoutext.split('_');
    if (keys.length > 1) {
        let maintag = keys[0].toLowerCase().trim();
        let childtag = keys[1].toLowerCase().trim();
        let speed = keys[2].toLowerCase().trim();
        let rhythm = keys[3].toLowerCase().trim();
        let tune = keys[4].toLowerCase().trim();
        let stage = keys[5].toLowerCase().trim();
        callback(null, maintag, childtag, speed, rhythm, tune, stage);
    }
    else {
        callback(filename + ' is invalid. must like 功能_风格类型_速度_拍号_调_乐段_随机数 音乐命名规范.wav');
    }
}

function addSoundFileNameToTags(filename, fileid, finish) {
    async.waterfall([
        (callback) => {
            parseSoundTag(filename, (err, maintag, childtag, tags) => {
                console.log(err, maintag, childtag, tags);
                callback(err, maintag, childtag, tags);
            });
        },

        (maintag, childtag, tags, callback) => {
            autodb.client(false).sadd(config.allmaintag, maintag, (err) => {
                callback(err, maintag, childtag, tags);
            })
        },

        (maintag, childtag, tags, callback) => {
            autodb.client(false).sadd(maintag + config.childtags, childtag, (err) => {
                callback(err, maintag, childtag, tags);
            })
        },

        (maintag, childtag, tags, callback) => {
            async.forEach(tags, (tag, cb) => {
                let grandsontagtable = maintag + '.' + childtag + config.grandsontags;
                autodb.client(false).sadd(grandsontagtable, tag, (err) => {
                    cb(err);
                })
            }, (err) => {
                callback(err, maintag, childtag, tags);
            })
        },

        (maintag, childtag, tags, callback) => {
            async.forEach(tags, (tag, cb) => {
                autodb.addtag(false, fileid, maintag, childtag, tag, (err) => {
                    log('addtag', fileid, tag);
                    cb(err);
                })
            }, (err) => {
                callback(err, maintag, childtag);
            })
        },

	(maintag, childtag, callback) => {
            autodb.client(false).zrange('all.names', 0, -1, (err, response) => {
                if (err) {
                    callback(err);
                } else {
                    async.forEach(response, (r, cb) => {
                        autodb.client(false).del(r, (err, response) => {
                            cb(err);
                        })
                    }, (err) => {
                        callback(err,maintag, childtag);
                    });
                }
            })
        }

    ], (err, maintag, childtag) => {
        finish(err, maintag, childtag);
    });
}

function addMusicFileNameToTags(filename, fileid, finish) {
    async.waterfall([
        (callback) => {
            parseMusicTag(filename, (err, maintag, childtag, speed, rhythm, tune, stage) => {
                callback(err, maintag, childtag, speed, rhythm, tune, stage);
            });
        },

        (maintag, childtag, speed, rhythm, tune, stage, callback) => {
            autodb.client(true).sadd(config.allmaintag, maintag, (err) => {
                callback(err, maintag, childtag, speed, rhythm, tune, stage);
            })
        },

        (maintag, childtag, speed, rhythm, tune, stage, callback) => {
            autodb.client(true).sadd(maintag + config.childtags, childtag, (err) => {
                callback(err, maintag, childtag, speed, rhythm, tune, stage);
            })
        },

        (maintag, childtag, speed, rhythm, tune, stage, callback) => {
            let grandsontagtable = maintag + '.' + childtag + config.grandsontags;
            autodb.client(true).sadd(grandsontagtable, tune, (err) => {
                callback(err, maintag, childtag, speed, rhythm, tune, stage);
            })
        },

        (maintag, childtag, speed, rhythm, tune, stage, callback) => {
            autodb.addtag(true, fileid, maintag, childtag, tune, (err) => {
                callback(err, maintag, childtag, speed, rhythm, tune, stage);
            })
        }
    ], (err, maintag, childtag, speed, rhythm, tune, stage) => {
        finish(err, maintag, childtag, speed, rhythm, tune, stage);
    });
}

function addNewSoundFile(filename, info, fileid, finish){
    async.waterfall([
        (callback) => {
            addSoundFileNameToTags(filename, fileid, (err, maintag, childtag) => {
                callback(err, info, maintag, childtag);
            });
        },

        (info, maintag, childtag, callback) => {
            var basename = path.basename(filename);
            var data = {};
            data.name = basename;
            data.size = info.size;
            data.import = new Date().getTime();
            data.duration = info.duration;
            data.samplerate = info.header.sample_rate;
            data.channels = info.header.num_channels;
            data.format = info.header.audio_format;
            data.bitspersample = info.header.bits_per_sample;
            data.downloadtimes = 0;
            data.stars = 0;
            data.maintag = maintag;
            data.childtag = childtag;
            data.suffix = path.extname(filename);
            autodb.setfileinfo(false, fileid, data, (err) => {
                callback(err);
            });
        },
    ], (err) => {
        if (err) {
            log(err);
        }
        finish(err);
    })
}


function addNewMusicFile(filename, info, fileid, finish){
    async.waterfall([
        (callback) => {
            addMusicFileNameToTags(filename, fileid, (err, maintag, childtag, speed, rhythm, tune, stage) => {
                callback(err, info, maintag, childtag, speed, rhythm, tune, stage);
            })
        },

        (info, maintag, childtag, speed, rhythm, tune, stage, callback) => {
            var basename = path.basename(filename);
            var data = {};
            data.name = basename;
            data.size = info.size;
            data.import = new Date().getTime();
            data.duration = info.duration;
            data.samplerate = info.header.sample_rate;
            data.channels = info.header.num_channels;
            data.format = info.header.audio_format;
            data.bitspersample = info.header.bits_per_sample;
            data.downloadtimes = 0;
            data.stars = 0;
            data.maintag = maintag;
            data.childtag = childtag;
            data.suffix = path.extname(filename);
            data.speed = speed;
            data.rhythm = rhythm;
            data.tune = tune;
            data.stage = stage;
            autodb.setfileinfo(true, fileid, data, (err) => {
                callback(err);
            });
        },
    ], (err) => {
        if (err) {
            log(err);
        }
        finish(err);
    })
}

function addnewfile(isMusic, fileid, filename, fullname, size, finish) {
    log('addnewfile', fileid, filename, size);

    async.waterfall([
        (callback) => {
            wfi.infoByFilename(fullname, function (err, info) {
                callback(err, info);
            })
        },

        (info,callback) => {
            if(isMusic){
                addNewMusicFile(filename,info,fileid,(err)=>{
                    callback(err);
                })
            }else{
                addNewSoundFile(filename,info,fileid,(err)=>{
                    callback(err);
                })
            }
        },

        (callback) => {
            autodb.client(isMusic).sadd(config.allfile, fileid, function (err, response) {
                log('sadd', 'allfile', fileid);
                callback(err);
            })
        },

        (callback) => {
            var resourcepath = isMusic ? config.musicResourcePath : config.soundResourcePath;
            dst = path.join(resourcepath, fileid + path.extname(filename));
            movefile(fullname, dst, (err) => {
                callback(err);
            });
        },
    ], (err) => {
        if (err) {
            log(err);
        }
        finish(err);
    })
}

function analyzefile(isMusic, rootpath, filename, callback) {
    var md5 = crypto.createHash('md5');
    var fullname = path.join(rootpath, filename);
    var stream = fs.createReadStream(fullname);
    stream.on('data', function (chunk) {
        md5.update(chunk);
    });
    stream.on('end', function () {
        var fileid = md5.digest('hex').toUpperCase();
        autodb.client(isMusic).get(fileid, function (err, response) {
            if (err) {
                log(fileid, err);
                callback(err);
            }
            else if (response) {
                log(response, fileid, 'allready exist');
                addSoundFileNameToTags(filename, fileid, (err) => {
                    if (err) {
                        callback(err);
                    } else {
                        if (config.debug) {
                            callback(err);
                        } else {
                            fs.unlink(fullname, (err) => {
                                log('remove', fullname, err);
                                callback(err);
                            });
                        }
                    }
                });
            } else {
                addnewfile(isMusic, fileid, filename, fullname, stream.bytesRead, (err) => {
                    callback(err);
                });
            }
        })
    });
}

function analyze_folder(rootpath, short, finish) {
    var allfiles = new Array();
    async.waterfall([
        //遍历目录
        (callback) => {
            fs.readdir(path.join(rootpath, short), function (err, paths) {
                callback(err, paths);
            })
        },
        function (paths, callback) {
            async.forEach(paths, function (file, cb) {
                log('file', file);
                var fullname = path.join(short, file);
                fs.stat(path.join(rootpath, short, file), function (err, info) {
                    if (err) {
                        cb(err);
                    } else if (info.isDirectory()) {
                        analyze_folder(rootpath, fullname, function (err, files) {
                            allfiles = allfiles.concat(files);
                            cb(err);
                        });
                    } else if (!file.startsWith('.') && file.endWith('.wav')) {
                        log(fullname);
                        allfiles.push(fullname);
                        cb(err);
                    }
                    else {
                        cb(null);
                    }
                })
            }, (err) => {
                callback(err);
            })
        },
    ], (err) => {
        if (err) {
            log(err);
        }
        if (finish) {
            finish(err, allfiles);
        }
    })
}

function reportProgress(isMusic, socket, progress) {
    var intProgress = Math.floor(progress);
    log('progress', intProgress);
    if (!config.debug) {
        var protocol = isMusic ? 'progress-music' : 'progress-sound';
        emit.send(socket, protocol, { value: intProgress });
    }
}

function onsocket(socket, data) {
    var importpath = data.isMusic ? config.musicImportPath : config.soundImportPath;
    analyze_folder(importpath, "", function (err, files) {
        var total = files.length;
        var index = 0;
        async.forEach(files, (file, cb) => {
            analyzefile(data.isMusic,importpath, file, (err) => {
                reportProgress(data.isMusic, socket, index * 100 / total);
                index++;
                cb(err);
            });
        }, (err) => {
            if (err) {
                log(err);
            }
            reportProgress(data.isMusic,socket, 100.5);
        })
    });
}

module.exports = {
    onsocket
}
